package com.example.performance.jmh.test;

import org.openjdk.jmh.annotations.*;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author zpMeng
 * @since 10/17/2024
 */
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(1)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class PrimitiveAndBoxedPerformanceCompare {

    @State(Scope.Thread)
    public static class ExecutionPlan {

        int len = 1_000_000;
        int[] primitiveArr = new int[len];
        Integer[] boxedArr = new Integer[len];
        Random rnd = new Random();

        @Setup(Level.Trial)
        public void setUp() {
            for (int i = 0; i < len; i++) {
                int val = rnd.nextInt();
                // do not generate 0 to ensure no division by zero
                if (val == 0) val = 1;
                primitiveArr[i] = val;
                boxedArr[i] = val;
            }
        }
    }

    //@Benchmark
    public int sumPrimitive(ExecutionPlan plan) {
        int result = 0;
        for (int i = 0; i < plan.len; i++) {
            result += plan.primitiveArr[i];
        }
        return result;
    }

    //@Benchmark
    public Integer sumBoxed(ExecutionPlan plan) {
        Integer result = 0;
        for (int i = 0; i < plan.len; i++) {
            result += plan.boxedArr[i];
        }
        return result;
    }


    //@Benchmark
    public int minusPrimitive(ExecutionPlan plan) {
        int result = 0;
        for (int i = 0; i < plan.len; i++) {
            result -= plan.primitiveArr[i];
        }
        return result;
    }

    //@Benchmark
    public Integer minusBoxed(ExecutionPlan plan) {
        Integer result = 0;
        for (int i = 0; i < plan.len; i++) {
            result -= plan.boxedArr[i];
        }
        return result;
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public int multiplyPrimitive(ExecutionPlan plan) {
        int result = 1;
        for (int i = 0; i < plan.len; i++) {
            result *= plan.primitiveArr[i];
        }
        return result;
    }

    //@Benchmark
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public Integer multiplyBoxed(ExecutionPlan plan) {
        Integer result = 1;
        for (int i = 0; i < plan.len; i++) {
            result *= plan.boxedArr[i];
        }
        return result;
    }

    @Benchmark
    public int dividePrimitive(ExecutionPlan plan) {
        int result = Integer.MAX_VALUE;
        for (int i = 0; i < plan.len; i++) {
            result /= plan.primitiveArr[i];
        }
        return result;
    }

    //@Benchmark
    public Integer divideBoxed(ExecutionPlan plan) {
        Integer result = Integer.MAX_VALUE;
        for (int i = 0; i < plan.len; i++) {
            result /= plan.boxedArr[i];
        }
        return result;
    }
}
